package com.clp.protocol.modbus_tcp.client;

import com.clp.protocol.core.common.connect.RecvCallback;
import com.clp.protocol.modbus_tcp.client.async.DefaultMasterPromise;
import com.clp.protocol.modbus_tcp.client.async.MasterFuture;
import com.clp.protocol.modbus_tcp.client.async.SendMasterFuture;
import com.clp.protocol.modbus_tcp.client.async.SendMasterPromise;
import com.clp.protocol.modbus_tcp.client.async.callback.RecvMbFrmCallback;
import com.clp.protocol.modbus_tcp.client.master_config.MasterConnConfig;
import com.clp.protocol.modbus_tcp.client.master_state.MasterState;
import com.clp.protocol.modbus_tcp.client.pipeline.AbstractModbusChannelInitializer;
import com.clp.protocol.modbus_tcp.connect.ConnInfoImpl;
import com.clp.protocol.modbus_tcp.definition.CoilSetting;
import com.clp.protocol.modbus_tcp.definition.CoilStatus;
import com.clp.protocol.modbus_tcp.definition.ConstVal;
import com.clp.protocol.modbus_tcp.mb_frame.MbFrm;
import com.clp.protocol.modbus_tcp.mb_frame.MbFrmFactory;
import com.clp.protocol.modbus_tcp.mb_frame.ReqMbFrm;
import com.clp.protocol.modbus_tcp.mb_frame.RespMbFrm;
import com.clp.protocol.modbus_tcp.mb_frame.mb_body.mb_data.resp_ok.*;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.util.Collection;
import java.util.List;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutorService;

@Slf4j
public class MasterImpl extends ConnInfoImpl implements Master {
    protected volatile int sendSeq; // 序号
    @Getter
    protected final AbstractModbusChannelInitializer initializer;
    // 状态列表
    protected final ConcurrentMap<MasterState.Type, MasterState> masterStateMap;

    public MasterImpl(Channel channel, MasterConnConfig cfg) {
        super(channel, cfg);
        this.initializer = cfg.getInitializer();
        this.masterStateMap = MasterState.newMasterStateMap(this, cfg);
    }

    @Override
    public void reset(Channel channel) {
        super.reset(channel);
    }

    @Override
    public int sendSeq() {
        return sendSeq;
    }

    /**
     * 增加发送序号
     */
    public synchronized void addSendSeqCircularly() {
        sendSeq = (sendSeq + 1) % ConstVal.MAX_SEQ;
    }

    public void startMonitorTasks() {
        masterStateMap.values().forEach(MasterState::startMonitorTasks);
    }

    public void stopMonitorTasks() {
        masterStateMap.values().forEach(MasterState::tryStopMonitorTasks);
    }

    @Override
    public ExecutorService executorService() {
        return channel.eventLoop();
    }

    public RespMbFrm updateStatesByRecving(RespMbFrm respMbFrm) {
        // 先更新连接状态
        respMbFrm = super.updateConnStatesByRecving(respMbFrm);
        // 再更新主站状态
        for (MasterState masterState : masterStateMap.values()) {
            respMbFrm = masterState.updateByRecving(respMbFrm);
        }
        return respMbFrm;
    }

    public ReqMbFrm updateStatesBySending(ReqMbFrm reqMbFrm) {
        // 先更新主站状态
        for (MasterState masterState : masterStateMap.values()) {
            reqMbFrm = masterState.updateBySending(reqMbFrm);
        }
        // 再更新连接状态
        reqMbFrm = super.updateConnStatesBySending(reqMbFrm);
        return reqMbFrm;
    }

    @Override
    public void addRecvMbFrmCallback(RecvMbFrmCallback callback) {
        recvMbFrmCallbacks.add(callback);
    }

    public void handleRecvMbFrmCallbacks(RespMbFrm respMbFrm) {
        channel.eventLoop().submit(() -> {
            this.recvMbFrmCallbacks.removeIf(callback -> callback.whenRecvMbFrm(respMbFrm));
        });
    }

    /**
     * 关闭通道
     */
    public MasterFuture<Void> closeChannel() {
        // 关闭通道
        DefaultMasterPromise<Void> closePromise = new DefaultMasterPromise<>(this);
        channel.close().addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture future) throws Exception {
                if (!future.isSuccess()) {
                    closePromise.setFailure(future.cause());
                }
                closePromise.setSuccess();
            }
        });
        return closePromise;
    }

    @Override
    public SendMasterFuture<ReadCoilStatusRespOkMbData> sendReqMbFrmOfReadCoilStatus(int startAddr, int num) {
        ReqMbFrm reqMbFrm = MbFrmFactory.getReqMbFrmOfReadCoilStatus(sendSeq, addr, startAddr, num);
        SendMasterPromise<ReadCoilStatusRespOkMbData> sendPromise = new SendMasterPromise<>(this, ReadCoilStatusRespOkMbData.class, sendSeq);
        // 注册
        masterStateMap.get(MasterState.Type.SEND_RECV).register(sendPromise);
        channel().writeAndFlush(reqMbFrm);
        return sendPromise;
    }

    @Override
    public SendMasterFuture<ReadInputStatusRespOkMbData> sendReqMbFrmOfReadInputStatus(int startAddr, int num) {
        ReqMbFrm reqMbFrm = MbFrmFactory.getReqMbFrmOfReadInputStatus(sendSeq, addr, startAddr, num);
        SendMasterPromise<ReadInputStatusRespOkMbData> sendPromise = new SendMasterPromise<>(this, ReadInputStatusRespOkMbData.class, sendSeq);
        // 注册
        masterStateMap.get(MasterState.Type.SEND_RECV).register(sendPromise);
        channel().writeAndFlush(reqMbFrm);
        return sendPromise;
    }

    @Override
    public SendMasterFuture<ReadHoldRegsRespOkMbData> sendReqMbFrmOfReadHoldRegs(int startAddr, int num) {
        ReqMbFrm reqMbFrm = MbFrmFactory.getReqMbFrmOfReadHoldRegs(sendSeq, addr, startAddr, num);
        SendMasterPromise<ReadHoldRegsRespOkMbData> sendPromise = new SendMasterPromise<>(this, ReadHoldRegsRespOkMbData.class, sendSeq);
        // 注册
        masterStateMap.get(MasterState.Type.SEND_RECV).register(sendPromise);
        channel().writeAndFlush(reqMbFrm);
        return sendPromise;
    }

    @Override
    public SendMasterFuture<ReadInputRegsRespOkMbData> sendReqMbFrmOfReadInputRegs(int startAddr, int num) {
        ReqMbFrm reqMbFrm = MbFrmFactory.getReqMbFrmOfReadInputRegs(sendSeq, addr, startAddr, num);
        SendMasterPromise<ReadInputRegsRespOkMbData> sendPromise = new SendMasterPromise<>(this, ReadInputRegsRespOkMbData.class, sendSeq);
        // 注册
        masterStateMap.get(MasterState.Type.SEND_RECV).register(sendPromise);
        channel().writeAndFlush(reqMbFrm);
        return sendPromise;
    }

    @Override
    public SendMasterFuture<ForceSingleCoilRespOkMbData> sendReqMbFrmOfForceSingleCoil(int setAddr, CoilSetting coilSetting) {
        ReqMbFrm reqMbFrm = MbFrmFactory.getReqMbFrmOfForceSingleCoil(sendSeq, addr, setAddr, coilSetting);
        SendMasterPromise<ForceSingleCoilRespOkMbData> sendPromise = new SendMasterPromise<>(this, ForceSingleCoilRespOkMbData.class, sendSeq);
        // 注册
        masterStateMap.get(MasterState.Type.SEND_RECV).register(sendPromise);
        channel().writeAndFlush(reqMbFrm);
        return sendPromise;
    }

    @Override
    public SendMasterFuture<PresetSingleRegRespOkMbData> sendReqMbFrmOfPresetSingleReg(int setAddr, int setVal) {
        ReqMbFrm reqMbFrm = MbFrmFactory.getReqMbFrmOfPresetSingleReg(sendSeq, addr, setAddr, setVal);
        SendMasterPromise<PresetSingleRegRespOkMbData> sendPromise = new SendMasterPromise<>(this, PresetSingleRegRespOkMbData.class, sendSeq);
        // 注册
        masterStateMap.get(MasterState.Type.SEND_RECV).register(sendPromise);
        channel().writeAndFlush(reqMbFrm);
        return sendPromise;
    }

    @Override
    public SendMasterFuture<ForceMultiCoilsRespOkMbData> sendReqMbFrmOfForceMultiCoils(int startAddr, List<CoilStatus> coilStatuses) {
        ReqMbFrm reqMbFrm = MbFrmFactory.getReqMbFrmOfForceMultiCoils(sendSeq, addr, startAddr, coilStatuses);
        SendMasterPromise<ForceMultiCoilsRespOkMbData> sendPromise = new SendMasterPromise<>(this, ForceMultiCoilsRespOkMbData.class, sendSeq);
        // 注册
        masterStateMap.get(MasterState.Type.SEND_RECV).register(sendPromise);
        channel().writeAndFlush(reqMbFrm);
        return sendPromise;
    }

    @Override
    public SendMasterFuture<ForceMultiRegsRespOkMbData> sendReqMbFrmOfForceMultiRegs(int startAddr, List<Integer> setVals) {
        ReqMbFrm reqMbFrm = MbFrmFactory.getReqMbFrmOfForceMultiRegs(sendSeq, addr, startAddr, setVals);
        SendMasterPromise<ForceMultiRegsRespOkMbData> sendPromise = new SendMasterPromise<>(this, ForceMultiRegsRespOkMbData.class, sendSeq);
        // 注册
        masterStateMap.get(MasterState.Type.SEND_RECV).register(sendPromise);
        channel().writeAndFlush(reqMbFrm);
        return sendPromise;
    }

    @Override
    public void addRecvCallback(RecvCallback<MbFrm> callback) {

    }

    @Override
    public void addRecvCallbacks(Collection<RecvCallback<MbFrm>> recvCallbacks) {

    }

    @Override
    public void removeRecvCallback(RecvCallback<MbFrm> callback) {

    }

    @Override
    public void removeRecvCallbacks(Collection<RecvCallback<MbFrm>> recvCallbacks) {

    }
}
